#include <vector>
#include <sstream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <pthread.h>

// 线程属性
struct ThreadAttr
{
    pthread_t tid;
    std::string threadName;
};

volatile int ticket = 100;
pthread_rwlock_t rwlock;

/*
lock();
readerNum++;
unlock();
$$读数据$$
lock();
readerNum--;
unlock();
*/

// 读者读票数 不对ticket做操作
void *reader_startRoutine(void *arg)
{
    char *readerName = (char *)arg;
    while (true)
    {
        pthread_rwlock_rdlock(&rwlock);
        if (ticket <= 0) // 没票了 不输出票数
        {
            pthread_rwlock_unlock(&rwlock);
            break;
        }
        // 有票
        printf("%s: remain :%d\n", readerName, ticket);
        pthread_rwlock_unlock(&rwlock);
        usleep(1);
    }
    return nullptr;
}

/*
lock();
if(readerNum > 0)
{
    unlock();
    return;
}
$$写数据$$

unlock();
*/

// 写者对ticket做操作
void *writer_startRoutine(void *arg)
{
    char *writerName = (char *)arg;
    while (true)
    {
        pthread_rwlock_wrlock(&rwlock);
        if (ticket <= 0) // 没票了 不能减少了
        {
            pthread_rwlock_unlock(&rwlock);
            break;
        }
        printf("%s: reduced :%d\n", writerName, --ticket);
        pthread_rwlock_unlock(&rwlock);
        usleep(1);
    }
    return nullptr;
}

// 拼接读者名称
std::string create_readerName(std::size_t index)
{
    // static const std::ios_base::openmode
    // std::ios_base::ate = (std::ios_base::openmode)2
    // Open and seek to end immediately after opening.
    std::ostringstream oss("reader-", std::ios_base::ate);
    oss << index;
    return oss.str();
}

// 拼接写者名称
std::string create_writerName(std::size_t index)
{
    std::ostringstream oss("writer-", std::ios_base::ate);
    oss << index;
    return oss.str();
}

// 创建读者线程
void create_readers(std::vector<ThreadAttr> &vec)
{
    for (std::size_t i = 0; i < vec.size(); ++i)
    {
        vec[i].threadName = create_readerName(i);
        pthread_create(&vec[i].tid, nullptr, reader_startRoutine, (void *)vec[i].threadName.c_str());
    }
}

// 创建写者线程
void create_writers(std::vector<ThreadAttr> &vec)
{
    for (std::size_t i = 0; i < vec.size(); ++i)
    {
        vec[i].threadName = create_writerName(i);
        pthread_create(&vec[i].tid, nullptr, writer_startRoutine, (void *)vec[i].threadName.c_str());
    }
}

// 逆序回收线程
void join_threads(std::vector<ThreadAttr> const &vec)
{
    for (std::vector<ThreadAttr>::const_reverse_iterator it = vec.rbegin();
         it != vec.rend(); ++it)
    {
        pthread_t const &tid = it->tid;
        pthread_join(tid, nullptr);
    }
}

// 设置读写优先级
void init_rwlock()
{
#ifdef WriteFirst // 写优先
    pthread_rwlockattr_t attr;
    pthread_rwlockattr_init(&attr);
    pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
    pthread_rwlock_init(&rwlock, &attr);
    pthread_rwlockattr_destroy(&attr);
#else // 读优先 会造成写饥饿
    pthread_rwlock_init(&rwlock, nullptr);
#endif
}

// 读者只读票 写者会减少票
int main()
{
    const std::size_t readerNum = 10;
    const std::size_t writerNum = 2;

    std::vector<ThreadAttr> readers(readerNum);
    std::vector<ThreadAttr> writers(writerNum);

    init_rwlock();

    create_readers(readers);
    create_writers(writers);

    join_threads(writers);
    join_threads(readers);

    pthread_rwlock_destroy(&rwlock);

    return 0;
}
